package 题目集.倍增算法.lca;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.List;


/**
 * 一共有n个节点，编号1 ~ n，一定有n-1条边连接形成一颗树 从一个点到另一个点的路径上有几条边，就需要耗费几个金币 每条查询(a, b,
 * c)表示有三个人分别站在a、b、c点上 他们想集合在树上的某个点，并且想花费的金币总数最少 一共有m条查询，打印m个答案 1 <= n <= 5 *
 * 10^5 1 <= m <= 5 * 10^5 测试链接 : https://www.luogu.com.cn/problem/P4281
 * https://www.acwing.com/problem/content/2888/
 * 题意：树上有3个点，问他们汇聚到一起，经过的最少边是多少。 如果是两个点，那么距离就是这两点到其最近公共祖先的距离。
 * 三个点的最近公共祖先求法：先求两个点的公共祖先，再用公共祖先与另一个点求公共祖先。
 * 三个点的最短距离，是让两个点先走到公共祖先位置，再由第三点走向这两点的最近公共祖先。 至于让哪两个点先走，就得看他们的公共祖先哪个较低。
 */
public class ch04_紧急集合 {
	static int maxn = (int) (5 * 1e5 + 1);
	static int n, m;
	static int[][] p;
	static int power;
	static int[] deep = new int[maxn];
	static List<Integer>[] graph = new List[maxn];

	public static void main(String[] args) {
		Reader sc = new Reader(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		// 相当于先除2，再向上取整，然后长度+1.
		power = (int) Math.ceil(Math.log(n) / Math.log(2));
		p = new int[n + 1][power];
		for (int i = 1; i <= n; i++) {
			graph[i] = new ArrayList<>();
		}
		for (int i = 1; i < n; i++) {
			int u = sc.nextInt();
			int v = sc.nextInt();
			graph[u].add(v);
			graph[v].add(u);
		}
		build(1);
		for (int i = 0; i < m; i++) {
			int[] cur = dis(sc.nextInt(), sc.nextInt(), sc.nextInt());
			System.out.println(cur[0] + " " + cur[1]);
		}
	}

	/**
	 * 最短距离为：由节点先走向低的公共祖先，再有剩下得节点走向高得公共祖先，再从高点走向低点。
	 */
	public static int[] dis(int a, int b, int c) {
		// 这一块关于a,b,c最近公共祖先只存在两个，以及下面high和low的选法的证明，请看ch04证明.txt
		int ac = lca(a, c);
		int ab = lca(a, b);
		int bc = lca(b, c);
		int high = (ab != ac) ? bc : ab;
		int low = (ab == ac) ? bc : (ab==bc?ac:ab);
		// 设ab为低点，他们三个距离为：d[a]+d[b]-2*d[low]+d[c]-d[high]+d[low]-d[h]
		// d[a]+d[b]-2*d[low]是a和b到低点的距离，d[c]-d[high]是c到高点的距离，d[low]-d[h]是高点到低点的距离
		// 化简得d[a]+d[b]+d[c]-2*d[high]-d[low]
		return new int[] { low, deep[a] + deep[b] + deep[c] - 2 * deep[high] - deep[low] };
	}
	


	public static int lca(int low, int high) {
		if (deep[high] > deep[low]) {
			low = low ^ high;
			high = low ^ high;
			low = low ^ high;
		}
		if (deep[high] != deep[low]) {
			// 深度逼近high
			for (int i = power - 1; i >= 0; i--) {
				// low跳跃后依然再high的下面
				if (deep[p[low][i]] > deep[high]) {
					low = p[low][i];
				}
			}
			low = p[low][0];
		}
		if (low == high) {
			return low;
		}
		for (int i = power - 1; i >= 0; i--) {
			if (p[low][i] != p[high][i]) {
				low = p[low][i];
				high = p[high][i];
			}
		}
		return p[low][0];
	}

	public static void build(int root) {
		int[] q = new int[n + 1];
		int h = 0, t = 0;
		int d = 0;
		p[root][0] = root;
		q[t++] = root;
		while (h < t) {
			int size = t - h;
			for (int i = 0; i < size; i++) {
				int cur = q[h++];
				deep[cur] = d;
				for (int next : graph[cur]) {
					if (next == p[cur][0])
						continue;
					p[next][0] = cur;
					q[t++] = next;
				}
			}
			d++;
		}
		// 填充j节点的2^i次方父亲
		// 2^i层父亲，相当于先走2^(i-1)层，再从2^(i-1)层走2^(i-1)层
		// 之前power和n的层次搞反了，调了好久
		for (int pow = 1; pow < power; pow++) {
			for (int cur = 1; cur <= n; cur++) {
				p[cur][pow] = p[p[cur][pow - 1]][pow - 1];
			}
		}
	}
    static class Reader {
        static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

        public Reader(Object... o) {
        }

        public int nextInt() {
            try {
                in.nextToken();
            } catch (Exception ignored) {
            }
            return (int) in.nval;
        }
    }
}